#include "sct/decode/videodec.h"

namespace sct
{
CVideoDec::CVideoDec(const CodecType codecType):
		CDecodeInterface(codecType),
		mCodec(NULL),
		mCodecCtx(NULL),
		mSwsCtx(NULL),
		mFrame(NULL),
		mImage(NULL),
		mSize(0)
{
}

void CVideoDec::init()
{
	switch(mCodecType)
	{
	case CT_H264:
	{
		mCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
		break;
	}

	default:
	{
		SCT_LOGD();
		break;
	}
	}

	if(NULL == mCodec)
	{
		SCT_LOGD("avcodec_find_decoder faied!");
		return;
	}
	SCT_LOGD("%s", mCodec->long_name);

	mCodecCtx = avcodec_alloc_context3(mCodec);

	int ret;
	ret = avcodec_open2(mCodecCtx, mCodec, NULL);
    if(ret<0)
	{
		SCT_LOGD("avcodec_open2 failed:%d", ret);
	}

	mFrame = av_frame_alloc();
	av_init_packet(&mPkt);
}

void CVideoDec::decode(const void * d, const int len, const int64_t pts)
{
	int ret = 0;

	mPkt.data = (uint8_t*)d;
	mPkt.size = len;
    mPkt.pts = pts;///access by: AVFrame::pkt_pts

	int nDecode;
	nDecode = avcodec_decode_video2(mCodecCtx, mFrame, &ret, &mPkt);
	if(nDecode != len)
	{
		throw -1;
	}

	if(ret)
	{
		//change
		if(mFrame->width < 100 || mFrame->height < 100 || NULL == mFrame->data[0])
		{
			SCT_LOGD();
			throw -1;
		}

		if(mSwsCtx)
		{
			sws_freeContext(mSwsCtx);
			mSwsCtx = NULL;
		}

		mSwsCtx = sws_getContext(mFrame->width, mFrame->height, mCodecCtx->pix_fmt,
				mFrame->width, mFrame->height, PIX_FMT_RGBA, SWS_FAST_BILINEAR, NULL, NULL, NULL);

		if(!mSwsCtx)
		{
			SCT_LOGD();
			throw -2;
		}

		if(mSize != mFrame->width*mFrame->height*4)
		{
			mSize = mFrame->width*mFrame->height*4;

			SCT_DELETE_ARRAY(mImage);
			mImage = new uint8_t[mSize];
		}

		uint8_t* dstData[4];
		int dstStride[4];

		dstData[0] = mImage;
		dstData[1] = NULL;
		dstData[2] = NULL;
		dstData[3] = NULL;

		dstStride[0] = mFrame->linesize[0] * 4;
		dstStride[1] = 0;
		dstStride[2] = 0;
		dstStride[3] = 0;


		if( sws_scale(mSwsCtx, mFrame->data, mFrame->linesize, 0,
				mFrame->height, dstData, dstStride) < 0)
		{
			SCT_LOGD();
			throw -3;
		}
		else
		{
			CBlockInfo info;
			info.width = mFrame->width;
			info.height = mFrame->height;
            info.pts = mFrame->pkt_pts;

			mCallback->onGenData(this, mImage, mSize, info);
		}
	}

}

CVideoDec::~CVideoDec()
{
	if(mCodecCtx)
	{
		avcodec_close(mCodecCtx);
		av_free(mCodecCtx);
		mCodecCtx = NULL;
	}

	if(mSwsCtx)
	{
		sws_freeContext(mSwsCtx);
		mSwsCtx = NULL;
	}

	if(mFrame)
	{
		av_frame_free(&mFrame);
		mFrame = NULL;
	}

	SCT_DELETE_ARRAY(mImage);
}
}
